import { getColor } from "./color";
import { ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT, ARROW_UP, IData, IPosition, IRect, TArrowKey } from "./type";
import { isRectsIntersect, UUID } from "./utils";
import { ZoomConfig } from "./zoomConfig";

export abstract class Data extends ZoomConfig {
    // 全部数据
    private _dataObject: { [id: string]: IData } = {};
    private _visiableIdSet: Set<string> = new Set();
    private _firstId: string = '';

    protected visiableLineIdsSet: Set<string> = new Set(); // 可见行的ID集合 小ID:大ID
    protected visiableLinesPosArray: { [id: string]: IPosition[] } = {}; // 可见行的坐标集合 id=>id1:id2
    protected canvasWidth: number = 0;
    protected canvasHeight: number = 0;
    protected visibleStartPosX: number = 0;
    protected visibleStartPosY: number = 0;

    protected get dataObject() {
        return { ...this._dataObject };
    }

    protected addData<T extends IData>(id: string, value: T) {
        this._dataObject[id] = value;
        this._visiableIdSet.add(id);
        if (!this._firstId) {
            this._firstId = id; // 记录第一个元素的id
        }
    }
    /**
     * 删除元素 
     * @param id 
     * @param delChildren true表示删除当前节点并删除子节点, false表示只删除当前节点 
     * @returns string 若有兄弟返回兄弟（优先上），无则返回父节点ID
     */
    protected delData(id: string, delChildren?: boolean): string | null {
        if (this._firstId === id) { return null; } // 不允许删除第一个元素
        let item = this._dataObject[id];
        if (!item || !item.parentId || !this.getElementById(item.parentId)) { return null; }
        const parentId = item.parentId;
        const parentItem = this._dataObject[parentId];
        const index = parentItem.children.ids.indexOf(id);
        if (index === -1) { return null; } // 如果父节点没有该子节点则不删除

        if (delChildren) {
            const childrenIds = this.getChildrenIds(id); // 子节点
            childrenIds.push(id); // 包含当前节点
            for (const childId of childrenIds) {
                delete this._dataObject[childId];
                this._visiableIdSet.delete(childId);
            }
            parentItem.children.ids.splice(index, 1); // 从父节点的子节点中删除当前节点
        } else {
            const childrenIds = [...item.children.ids];
            parentItem.children.ids.splice(index, 1, ...childrenIds); // 从父节点的子节点中删除当前节点
            for (const childId of childrenIds) {
                const childItem = this._dataObject[childId];
                if (childItem) {
                    childItem.parentId = parentId; // 更新子节点的父节点ID
                }
            }
            delete this._dataObject[id]; // 删除当前节点
        }

        return parentItem.children.ids[index - 1] || parentItem.children.ids[index] || parentId;
    }
    private getChildrenIds(id: string): string[] {
        const childrenIds: string[] = [];
        const item = this._dataObject[id];
        if (!item) { return childrenIds; }
        for (const childId of item.children.ids) {
            childrenIds.push(childId);
            childrenIds.push(...this.getChildrenIds(childId)); // 递归获取子节点
        }
        return childrenIds;
    }

    protected visibleIds() {
        return Array.from(this._visiableIdSet);
    }

    protected visibleElements() {
        const ids = this.visibleIds();
        return ids.map(id => this._dataObject[id]).filter(item => item);
    }

    /**
     * 移动节点，并设置可视区域的节点
     * @param xm 
     * @param ym 
     * @param ids 移动选中节点
     * @returns 
     */
    protected moveElesCalculateVisible(xm: number, ym: number, ids?: string[]) {
        ids = ids || Object.keys(this._dataObject);
        const { visibleStartPosX, visibleStartPosY, canvasWidth, canvasHeight } = this;
        for (const id of ids) {
            const item = this._dataObject[id];
            if (item && (xm || ym)) {
                item.x += xm;
                item.y += ym;
                const { lineChars } = item;
                for (const line of lineChars) {
                    for (const char of line.chars) {
                        char.x += xm;
                        char.y += ym;
                        char.ry += ym; // 更新相对y坐标
                    }
                    line.x += xm;
                    line.y += ym;
                }
            }
            const { x, y, w, h } = item;
            if (x + w >= visibleStartPosX && x <= canvasWidth && y + h >= visibleStartPosY && y <= canvasHeight) {
                this._visiableIdSet.add(id);
            } else {
                this._visiableIdSet.delete(id);
            }
        }
    }
    /**
     * 计算可视区域的连线位置
     */
    protected calculateVisibleLinesPos() {
        const ids = Object.keys(this._dataObject);
        this.visiableLineIdsSet.clear(); // 清空可见行ID集合
        this.visiableLinesPosArray = {}; // 清空可见行坐标集合
        for (const id of ids) {
            const item = this._dataObject[id];
            if (!item) { continue; }
            const pos0 = { x: item.x + item.w, y: item.y + item.h / 2 };
            for (const childId of item.children.ids) {
                const childEle = this._dataObject[childId];
                if (childEle) {
                    const pos3 = { x: childEle.x, y: childEle.y + childEle.h / 2 };
                    const pos1 = { x: (pos0.x + pos3.x) / 2, y: pos0.y };
                    const pos2 = { x: pos1.x, y: pos3.y };
                    const posArray: IPosition[] = [pos0, pos1, pos2, pos3];
                    const posArray2: IPosition[] = [];
                    posArray.forEach(t => {
                        posArray2.push({
                            x: t.x,
                            y: t.y,
                        });
                    });
                    const lineId = this._genarateLineId(id, childId);
                    if (this.isLinePosVisible(posArray2)) {
                        this.visiableLinesPosArray[lineId] = posArray; // 记录可见线的坐标
                        this.visiableLineIdsSet.add(lineId);
                    }
                }
            }
        }
    }
    // 判断线段是否可见
    private isLinePosVisible(posArray: IPosition[]): boolean {
        if (!posArray || posArray.length < 2) {
            return false; // 至少需要两个点
        }
        const { defaultLineWidth } = this.defaultLineWidth();
        const halfRectSize = defaultLineWidth / 2;
        const rectSize = defaultLineWidth; // 矩形大小
        const rect1 = { x: 0, y: 0, w: this.canvasWidth, h: this.canvasHeight };
        for (let i = 0; i < posArray.length - 1; i++) {
            const pos0 = posArray[i];
            const pos1 = posArray[i + 1];
            const rect0: IRect = i % 2 === 0 ? {
                x: pos0.x,
                y: pos0.y - halfRectSize,
                w: pos1.x - pos0.x,
                h: rectSize
            } : {
                x: pos0.x - halfRectSize,
                y: pos0.y,
                w: rectSize,
                h: pos1.y - pos0.y
            };
            if (isRectsIntersect(rect0, rect1)) {
                return true; // 水平线段可见
            }
        }

        return false;
    }
    // 根据坐标获取元素
    protected getElementByXY(x: number, y: number): IData | undefined {
        for (const item of this.visibleElements()) {
            if (x >= item.x && x <= item.x + item.w && y >= item.y && y <= item.y + item.h) {
                return item;
            }
        }
        return undefined;
    }
    protected getElementById(id: string): IData | undefined {
        return this._dataObject[id];
    }
    /**
     * 核心算法
     * 
     * 计算各个节点之间的位置
     */
    protected calculatePos() {
        const id = this._firstId;
        const levelIdMap: Map<number, string[]> = new Map().set(0, [id]); // x轴每层的节点ID
        this._getLevelMapCalculateMaxWidthAddHeightGap(id, levelIdMap, 1);
        this._setPosX(levelIdMap); // 设置位置 x
        this._calculateMaxHeight(levelIdMap); // 计算高度
        this._setPosY(levelIdMap); // 设置位置 y
        this._subtractGAPRender();
    }
    private _getLevelMapCalculateMaxWidthAddHeightGap(id: string, levelMap: Map<number, string[]>, level: number) {
        if (!id || !this._dataObject[id]) {
            return;
        }
        const data = this._dataObject[id];
        data.h += this.ELES_GAP_VERTICAL; // 每个节点的高度增加间距
        const children = data.children;
        const ids = children.ids.length > 0 ? [...children.ids] : [];
        children.mw = Math.max(...ids.map(t => this._dataObject[id].w), 0); // 叶子节点的子宽度为0
        if (!levelMap.has(level)) {
            levelMap.set(level, ids);
        } else {
            (levelMap.get(level) || []).push(...ids);
        }
        level++;
        for (const tId of ids) {
            this._getLevelMapCalculateMaxWidthAddHeightGap(tId, levelMap, level);
        }
    }
    private _calculateMaxHeight(levelIdMap: Map<number, string[]>) {
        const len = levelIdMap.size;
        for (let i = len - 2; i >= 0; i--) { // 最后一层为空
            const levelIds = levelIdMap.get(i) || [];
            for (const lId of levelIds) {
                const lEle = this._dataObject[lId];
                if (!lEle.parentId || !this._dataObject[lEle.parentId]) {
                    continue;
                }
                const pEle = this._dataObject[lEle.parentId];
                const children = pEle.children;
                children.mh = children.ids.length > 0 ? children.ids
                    .map(t => Math.max(this._dataObject[t].h, this._dataObject[t].children.mh))
                    .reduce((a, b) => a + b, 0) :
                    0; // 叶子节点的子高度为0
            }
        }
    }
    private _setPosX(levelIdMap: Map<number, string[]>) {
        // 计算x值
        let maxWX = this._dataObject[this._firstId].x; // 最大宽度位置
        for (let i = 1; i < levelIdMap.size - 1; i++) { // 掐头去尾：第一层为主，最后一层为空
            const levelIds = levelIdMap.get(i) || [];
            const prevIds = levelIdMap.get(i - 1) || [];
            const prevMW = Math.max(...prevIds.map(t => this._dataObject[t].w));
            maxWX += prevMW + this.ELES_GAP_HORIZONTAL;
            levelIds.forEach((lId) => {
                const lEle = this._dataObject[lId];
                const modifyX = maxWX - lEle.x;
                lEle.x = maxWX;
                lEle.lineChars.forEach(line => {
                    line.x += modifyX; // 更新行的x坐标
                    line.chars.forEach(char => {
                        char.x += modifyX; // 更新字符的x坐标
                        char.ry += modifyX; // 更新字符的相对x坐标
                    });
                });
            });
        }
    }
    private _setPosY(levelIdMap: Map<number, string[]>) {
        for (let i = 1; i < levelIdMap.size; i++) {
            const levelIds = levelIdMap.get(i);
            if (!levelIds || levelIds.length === 0) {
                continue; // 没有节点
            }
            let startY = 0;
            let parentId = '';
            for (let j = 0; j < levelIds.length; j++) {
                const lId = levelIds[j];
                const { parentId: tParentId } = this._dataObject[lId];
                if (parentId !== tParentId) {
                    parentId = tParentId; // 更新父节点ID
                    const tParentEle = this._dataObject[tParentId];
                    startY = tParentEle.y + tParentEle.h / 2 - tParentEle.children.mh / 2; // 起始y值
                }
                const lEle = this._dataObject[lId];
                const tMaxH = Math.max(lEle.h, lEle.children.mh); // 当前节点的最大高度
                const newY = startY + tMaxH / 2 - lEle.h / 2; // 设置节点的y值
                const modifyY = newY - lEle.y; // 计算y坐标的偏移量
                lEle.y = newY;
                startY += tMaxH; // 更新起始y值
                lEle.lineChars.forEach(line => {
                    line.y += modifyY; // 更新行的y坐标
                    line.chars.forEach(char => {
                        char.y += modifyY; // 更新字符的y坐标
                        char.ry += modifyY; // 更新字符的相对y坐标
                    });
                });
            }
        }
    }
    private _subtractGAPRender() {
        const elesGapVertical = this.ELES_GAP_VERTICAL;
        for (const k in this._dataObject) {
            const d = this._dataObject[k];
            d.h -= elesGapVertical;
        }
    }

    // 可见行的ID集合
    private _genarateLineId(parentId: string, id: string): string {
        return Number(parentId) < Number(id)
            ? (parentId + ':' + id)
            : (id + ':' + parentId);
    }
    /**
     * 根据方向键添加节点 若返回ID即为新增ID
     * @param id 
     * @param arrow 
     * @param rectHeight 
     * @returns 
     */
    protected addNodeByDirection(id: string, arrow: TArrowKey, rectHeight: number): string | null {
        const ele = this.getElementById(id);
        if (!ele) { return null; }
        const parentEle = ele.parentId ? this.getElementById(ele.parentId) : null;

        const uId = UUID();
        let parentId = '';
        let childrenIds: string[] = [];
        if (arrow === ARROW_RIGHT) {
            childrenIds = [...ele.children.ids];
            childrenIds.forEach((tId) => {
                const tEle = this.getElementById(tId);
                if (tEle) {
                    tEle.parentId = uId; // 更新子节点的父节点ID
                }
            });
            parentId = ele.id;
            ele.children.ids = [uId];
        }
        else if (arrow === ARROW_LEFT) {
            if (!parentEle) { return null; } // 如果父节点不存在则不添加
            const index = parentEle.children.ids.indexOf(id);
            parentEle.children.ids.splice(index, 1, uId);
            ele.parentId = uId;
            parentId = parentEle.id;
            childrenIds = [id];
        }
        else if (arrow === ARROW_UP || arrow === ARROW_DOWN) {
            if (!parentEle) { return null; }
            const index = parentEle.children.ids.indexOf(id);
            parentEle.children.ids.splice(index + (arrow === ARROW_DOWN ? 1 : 0), 0, uId); // 在当前元素前插入新元素
            parentId = parentEle.id;
        } else {
            return null;
        }
        const { bg, color } = getColor();
        this.addData(uId, {
            id: uId,
            w: this.DEFAULT_RECT_MIN_WIDTH,
            h: rectHeight,
            x: 0,
            y: 0,
            baseChars: [],
            lineChars: [],
            parentId,
            bg,
            color,
            children: {
                ids: childrenIds,
                mh: 0,
                mw: 0,
            },
        });

        return uId;
    }
    /**
     * 移动某个节点到可视区域
     * @param id 
     * @returns 
     */
    protected moveEleVisible(id: string) {
        const ele = this.getElementById(id);
        if (!ele) { return; }
        const mGAP = this.SELECTED_RECT_PADDING * 2; // 选中矩形的内边距
        const { x, y, w, h } = ele;
        const { visibleStartPosX, visibleStartPosY, canvasWidth, canvasHeight } = this;
        let mx = 0, my = 0;
        if (x < visibleStartPosX) {
            mx = visibleStartPosX - x + mGAP;
        } else if (x + w > canvasWidth) {
            mx = canvasWidth - (w + x) - mGAP;
        }
        if (y < visibleStartPosY) {
            my = visibleStartPosY - y + mGAP;
        } else if (y + h > canvasHeight) {
            my = canvasHeight - (h + y) - mGAP;
        }
        if (mx !== 0 || my !== 0) {
            this.moveElesCalculateVisible(mx, my);
            this.calculateVisibleLinesPos(); // 重新计算可见线的位置
        }
    }
    protected get firstId() {
        return this._firstId;
    }
}